#ifndef asm_h__
#define asm_h__

#include <string>
#include <vector>
#include <iomanip>
#include <sstream>

namespace CCompiler
{
    namespace Asm
    {
        class Cmd 
        {
        public:                
            virtual const std::string str() = 0;
            virtual ~Cmd() {};
        };
        class Db: public Cmd
        {
            std::string name_;
            size_t size_;
        public:
            Db(const std::string name, size_t size): name_(name), size_(size) {}
            virtual const std::string str()
            {
                std::ostringstream stream;
                stream << name_ << " db " << size_ << " dup(0)";
                return stream.str();
            }                                                   
        };
        class Label: public Cmd
        {
            std::string name_;
        public:
            Label(const std::string name): name_(name) {}
            virtual const std::string str()
            {
                return name_ + ":";
            }
        };
        class Arg: public Cmd
        {
        public:
            virtual Arg *clone() = 0;
        };
        class Dir: public Cmd
        {
        protected:
            std::string dir_;
        public:
            Dir(std::string dir): dir_(dir) {}
        };
        class SizeDir: public Dir
        {
            Arg *arg_;
        public:
            SizeDir(std::string szdir, Arg *arg): Dir(szdir), arg_(arg) {}
            SizeDir(const SizeDir &obj): Dir(obj.dir_), arg_(obj.arg_) { const_cast<SizeDir &>(obj).arg_ = 0; }
            virtual const std::string str()
            {
                return dir_ + arg_->str();
            }                                                   
            ~SizeDir() { delete arg_; }
        };
        class Reg: public Arg
        {
        protected:
            std::string reg_;
        public:
            Reg(const std::string &reg): reg_(reg) {}
            virtual Arg *clone() { return new Reg(*this); }
            virtual const std::string str()
            {
                return reg_;
            }
        };
        class OffsetReg: public Reg
        {
            int absOffset_;
        public:
            OffsetReg(const std::string &reg, size_t absOffset = 0): 
              Reg(reg), absOffset_(absOffset) {}
            OffsetReg(const Reg &reg): Reg(reg), absOffset_(0) {}
            OffsetReg &setAbsOffset(int offset) { absOffset_ = offset; return *this;}
            virtual Arg *clone() { return new OffsetReg(*this); }
            virtual const std::string str()
            {
                std::ostringstream stream;
                stream << '[' << reg_;
                if(absOffset_ > 0) stream << " + " << absOffset_ << ']';
                else if(absOffset_ < 0) stream << " - " << -absOffset_ << ']';
                else stream << ']';
                return stream.str();
            }
        };
        class Offset: public Arg
        {
        protected:
            std::string mem_;
        public:
            Offset(const std::string &mem): mem_(mem) {}
            virtual Arg *clone() { return new Offset(*this); } 
            virtual const std::string str()
            {
                return mem_;
            }
        };
        class Mem: public Offset
        {
        public:
            Mem(const std::string &mem): Offset(mem) {}
            virtual Arg *clone() { return new Mem(*this); }
            virtual const std::string str()
            {
                return '[' + mem_ + ']';
            }
        };
        template<typename T>
        class Imm: public Arg // stands for immediate value
        {
            T val_;
        public:
            Imm(T val): val_(val) {}
            virtual Arg *clone() { return new Imm<T>(*this); }
            virtual const std::string str()
            {
                std::ostringstream stream;
                stream << std::fixed << val_;
                return stream.str();
            }
        };
        class Op: public Cmd
        {
            std::string mnem_;
            std::vector<Cmd *> args_; 
        public:
            Op(const std::string &mnem): mnem_(mnem) {}
            Op& add(Cmd *a) { args_.push_back(a); return *this;}
            virtual ~Op()
            {
                for(std::vector<Cmd *>::iterator i = args_.begin(); i != args_.end(); ++i)
                    delete (*i);
            }
            virtual const std::string str()
            {
                if(!args_.size()) return mnem_;
                std::string res = mnem_ + " ";                
                for(size_t i = 0; i < args_.size() - 1; ++i)
                {
                    res += args_[i]->str() + ", ";
                }
                return res += args_.back()->str();
            }

        };

    }
}

#endif // asm_h__
